﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SDS.SPConnectors.SPList.BDCModelGenerator
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += new EventHandler(Form1_Load);
            this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
        }

        #region Event Handlers - Form

        private void Form1_Load(object sender, EventArgs e)
        {
            this.txtDomain.Text = System.Environment.UserDomainName;
            this.txtUserName.Text = System.Environment.UserName;
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.ctx != null)
                SPSite_Disconnect();
        }

        #endregion
        #region Event Handlers - Controls

        private void chkRequiresAuthentication_CheckedChanged(object sender, EventArgs e)
        {
            AuthenticationOptions_Refresh();
        }
        private void btnConnectSite_Click(object sender, EventArgs e)
        {
            if (ctx == null)
                SPSite_Connect();
            else
                SPSite_Disconnect();
        }
        private void cmbSiteLists_SelectedIndexChanged(object sender, EventArgs e)
        {
            SPList_Connect();
        }
        private void chkAdvancedOptions_CheckedChanged(object sender, EventArgs e)
        {
            dgvcStaticName.Visible = chkAdvancedOptions.Checked;
            dgvcType.Visible = chkAdvancedOptions.Checked;
            dgvcIsIdentifier.Visible = chkAdvancedOptions.Checked;
            dgvcFormatString.Visible = chkAdvancedOptions.Checked;
        }
        private void btnGenerate_Click(object sender, EventArgs e)
        {
            string BDCM = GenerateModel();
            Clipboard.SetText(BDCM);
            MessageBox.Show("Model was copied to the clipboard");
        }

        #endregion

        #region Protected Helper Functions

        protected struct dgvRecord
        {
            public string Name { get; set; }
            public string StaticName { get; set; }
            public Type DataType { get { return (String.IsNullOrWhiteSpace(DataTypeString)) ? null : Type.GetType(DataTypeString, false, true); } }
            public string DataTypeString { get; set; }
            public string FormatString { get; set; }
            public bool? IsIdentifier { get; set; }
            public bool? ShowInFinder { get; set; }
            public bool? ShowInSpecificFinder { get; set; }
            public bool? FinderFilter { get; set; }
        }
        private Func<DataGridViewRow, dgvRecord> ToRecord 
        {
            get
            {
                return new Func<DataGridViewRow, dgvRecord>(x => new dgvRecord
                                                      {
                                                          Name = x.Cells[dataGridView1.Columns.IndexOf(dgvcField)].Value as string,
                                                          StaticName = x.Cells[dataGridView1.Columns.IndexOf(dgvcStaticName)].Value as string,
                                                          DataTypeString = x.Cells[dataGridView1.Columns.IndexOf(dgvcType)].Value as string,
                                                          FormatString = x.Cells[dataGridView1.Columns.IndexOf(dgvcFormatString)].Value as string,
                                                          IsIdentifier = (bool?) x.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value,
                                                          ShowInFinder = (bool?)x.Cells[dataGridView1.Columns.IndexOf(dgvcShowInFinder)].Value,
                                                          ShowInSpecificFinder = (bool?)x.Cells[dataGridView1.Columns.IndexOf(dgvcShowInSpecificFinder)].Value,
                                                          FinderFilter = (bool?)x.Cells[dataGridView1.Columns.IndexOf(dgvcFinderFilter)].Value
                                                      });
            }
        }

        protected IEnumerable<dgvRecord> dgvr_Finder
        {
            get
            {
                return dataGridView1.Rows.Cast<DataGridViewRow>()
                                    .Where(gvr => ((gvr.Cells[dataGridView1.Columns.IndexOf(dgvcShowInFinder)].Value != null &&
                                                    (bool)gvr.Cells[dataGridView1.Columns.IndexOf(dgvcShowInFinder)].Value) ||
                                                   (gvr.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value != null &&
                                                    (bool)gvr.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value)))
                                    .Select(gvr => ToRecord(gvr));
            }
        }
        protected IEnumerable<dgvRecord> dgvr_Finder_Filters
        {
            get
            {
                return dataGridView1.Rows.Cast<DataGridViewRow>()
                                    .Where(gvr => gvr.Cells[dataGridView1.Columns.IndexOf(dgvcFinderFilter)].Value != null)
                                    .Where(gvr => (bool)gvr.Cells[dataGridView1.Columns.IndexOf(dgvcFinderFilter)].Value)
                                    .Select(gvr => ToRecord(gvr));
            }
        }
        protected IEnumerable<dgvRecord> dgvr_SpecificFinder
        {
            get
            {
                return dataGridView1.Rows.Cast<DataGridViewRow>()
                                    .Where(gvr => ((gvr.Cells[dataGridView1.Columns.IndexOf(dgvcShowInSpecificFinder)].Value != null &&
                                                    (bool)gvr.Cells[dataGridView1.Columns.IndexOf(dgvcShowInSpecificFinder)].Value) ||
                                                   (gvr.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value != null &&
                                                    (bool)gvr.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value)))
                                    .Select(gvr => ToRecord(gvr));
            }
        }
        protected IEnumerable<dgvRecord> dgvr_SpecificFinder_Filters
        {
            get
            {
                return dataGridView1.Rows.Cast<DataGridViewRow>()
                                    .Select(gvr => ToRecord(gvr))
                                    .Where(r => r.IsIdentifier.HasValue && r.IsIdentifier.Value);
            }
        }
        protected IEnumerable<dgvRecord> dgvr_Enumerator
        {
            get
            {
                return dataGridView1.Rows.Cast<DataGridViewRow>()
                                    .Select(gvr => ToRecord(gvr))
                                    .Where(r => r.IsIdentifier.HasValue && r.IsIdentifier.Value);
            }
        }

        protected string GetFieldDataTypeString(Microsoft.SharePoint.Client.Field tField)
        {
            return tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Boolean ? "System.Boolean"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.DateTime ? "System.DateTime"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Integer ? "System.Int32"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Number ? "System.Double"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Counter ? "System.Int32"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Choice ? "System.String"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.MultiChoice ? "System.String"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Note ? "System.String"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Text ? "System.String"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Currency ? "System.Double"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Guid ? "System.Guid"
                : null;
        }
        protected Type GetFieldDataType(Microsoft.SharePoint.Client.Field tField)
        {
            return tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Boolean ? typeof (System.Boolean)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.DateTime ? typeof (System.DateTime)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Integer ? typeof (Nullable<System.Int32>)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Number ? typeof (System.Double)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Counter ? typeof(System.Int32)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Choice ? typeof (System.String)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.MultiChoice ? typeof(System.String)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Note ? typeof(System.String)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Text ? typeof(System.String)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Currency ? typeof(System.Double)
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Guid ? typeof(System.Guid)
                : null;
        }
        protected string GetFieldDataFormatString(Microsoft.SharePoint.Client.Field tField)
        {
            return tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.DateTime ? "{0:d}"
                : tField.FieldTypeKind == Microsoft.SharePoint.Client.FieldType.Currency ? "{0:c}"
                : null;
        }

        #endregion

        #region Private Helper Methods

        private Microsoft.SharePoint.Client.ClientContext ctx = null;

        private void SPSite_Connect()
        {
            // Validation
            if (String.IsNullOrWhiteSpace(txtSiteUrl.Text))
            {
                MessageBox.Show("No URL was specified!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // Initialization
            var Creds = !chkRequiresAuthentication.Checked
                        ? null
                        : String.IsNullOrWhiteSpace(txtUserName.Text) ||
                          String.IsNullOrWhiteSpace(txtPassword.Text)
                        ? System.Net.CredentialCache.DefaultNetworkCredentials
                        : String.IsNullOrWhiteSpace(txtDomain.Text)
                        ? new System.Net.NetworkCredential(txtUserName.Text, txtPassword.Text)
                        : new System.Net.NetworkCredential(txtUserName.Text, txtPassword.Text, txtDomain.Text);

            try
            {
                // Connect to SharePoint, load lists
                ctx = new Microsoft.SharePoint.Client.ClientContext(txtSiteUrl.Text);
                ctx.Credentials = Creds;
                ctx.AuthenticationMode = chkRequiresAuthentication.Checked
                                       ? Microsoft.SharePoint.Client.ClientAuthenticationMode.Default
                                       : Microsoft.SharePoint.Client.ClientAuthenticationMode.Anonymous;
                ctx.Load(ctx.Web.Lists);
                ctx.ExecuteQuery();

                // populate ComboBox with Lists
                cmbSiteLists.Items.Clear();
                ctx.Web.Lists.ToList().ForEach(l => cmbSiteLists.Items.Add(new KeyValuePair<string, Guid>(l.Title, l.Id)));
                cmbSiteLists.Enabled = true;

                // UI updates
                txtSiteUrl.Enabled = false;
                AuthenticationOptions_Refresh();
                btnConnectSite.Text = "Disconnect";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                SPSite_Disconnect();
                return;
            }
        } // void SPSite_Connect()
        private void SPSite_Disconnect()
        {
            ctx.Dispose();
            ctx = null;
            cmbSiteLists.Items.Clear();
            cmbSiteLists.Enabled = false;
            txtSiteUrl.Enabled = true;
            AuthenticationOptions_Refresh();
            btnConnectSite.Text = "Connect";
        } // void SPSite_Disconnect()

        private void SPList_Connect()
        {
            try
            {
                dataGridView1.Rows.Clear();

                var List = ctx.Web.Lists.ToList().Single(x => x.Id == (Guid)(((KeyValuePair<string, Guid>)cmbSiteLists.SelectedItem).Value));
                ctx.Load(List.Fields);
                ctx.ExecuteQuery();

                foreach (var tField in List.Fields.ToList())
                {
                    if (!IsHidden(tField) ||
                        tField.StaticName.ToLower() == "id")
                    {
                        var gvr = new DataGridViewRow();
                        gvr.CreateCells(dataGridView1);
                        gvr.Cells[dataGridView1.Columns.IndexOf(dgvcField)].Value = tField.Title;
                        gvr.Cells[dataGridView1.Columns.IndexOf(dgvcStaticName)].Value = StaticNameDecode(tField.StaticName);
                        gvr.Cells[dataGridView1.Columns.IndexOf(dgvcType)].Value = GetFieldDataTypeString(tField);
                        gvr.Cells[dataGridView1.Columns.IndexOf(dgvcFormatString)].Value = GetFieldDataFormatString(tField);
                        gvr.Cells[dataGridView1.Columns.IndexOf(dgvcIsIdentifier)].Value = tField.StaticName.ToLower() == "id";

                        if (!String.IsNullOrEmpty(gvr.Cells[dataGridView1.Columns.IndexOf(dgvcType)].Value as string))
                            dataGridView1.Rows.Add(gvr);
                    }
                } // foreach tField
            } // try
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        } // void SPList_Connect()

        private void AuthenticationOptions_Refresh()
        {
            this.chkRequiresAuthentication.Enabled = ctx == null;
            this.txtUserName.Enabled = chkRequiresAuthentication.Checked && ctx == null;
            this.txtPassword.Enabled = chkRequiresAuthentication.Checked && ctx == null;
            this.txtDomain.Enabled = chkRequiresAuthentication.Checked && ctx == null;
        } // void AuthenticationOptions_Refresh()

        #endregion
        #region Private Helper Functions

        private object Default(Type t)
        {
            return (t.IsValueType ? Activator.CreateInstance(t) : null);
        }
        private static string StaticNameEncode(string inp)
        {
            // for each character in the string, check whether it needs to be encoded.. if so, encode and ToLower
            return String.Join("",
                               inp.ToList()
                                  .Select(c => System.Xml.XmlConvert.EncodeName(c.ToString()) == c.ToString()
                                              ? c.ToString()
                                              : System.Xml.XmlConvert.EncodeName(c.ToString()).ToLower())
                                  .ToArray()
                              );
        }
        private static string StaticNameDecode(string inp)
        {
            // for each character in the string, check whether it needs to be encoded.. if so, encode and ToLower
            return System.Xml.XmlConvert.DecodeName(inp);
        }

        private bool IsHidden(Microsoft.SharePoint.Client.Field tField)
        {
            return tField.Hidden
                ? true
                : System.Xml.Linq.XElement.Parse(tField.SchemaXml).Attribute("Hidden") == null
                ? false
                : Boolean.Parse(System.Xml.Linq.XElement.Parse(tField.SchemaXml).Attribute("Hidden").Value);
        }

        private string GenerateModel()
        {
            StringBuilder sb = new StringBuilder();

            string prefix = "\r\n";

            sb.Append("<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?>");
            sb.AppendFormat("{0}<Model xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://schemas.microsoft.com/windows/2007/BusinessDataCatalog http://schemas.microsoft.com/windows/2007/BusinessDataCatalog/BDCMetadata.xsd\" xmlns=\"http://schemas.microsoft.com/windows/2007/BusinessDataCatalog\" Name=\"{1}\">", prefix, txtModelName.Text);
            
            sb.Append(GenerateLobSystems(String.Format("{0}  ", prefix)));

            sb.AppendFormat("{0}</Model>", prefix);

            return sb.ToString();
        }
        private string GenerateLobSystems(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            string LobSystemName = this.txtLobSystemName.Text;
            string LobSystemInstanceName = this.txtLobSystemInstanceName.Text;

            sb.AppendFormat("{0}<LobSystems>", prefix);
            sb.AppendFormat("{0}  <LobSystem Name=\"{1}\" Type=\"Custom\">", prefix, LobSystemName);

            // configure the connector
            sb.AppendFormat("{0}    <Properties>", prefix);
            sb.AppendFormat("{0}      <!-- SystemUtilityTypeName.value == IAdministrableSystem.AdministrableLobSystemInstanceProperties -->", prefix);
            sb.AppendFormat("{0}      <Property Name=\"SystemUtilityTypeName\" Type=\"System.String\">SDS.SPConnectors.SPList.SPListConnector, SDS.SPConnectors.SPList, Version=1.0.0.0, Culture=neutral, PublicKeyToken=afb1c3617d085673</Property>", prefix);
            sb.AppendFormat("{0}    </Properties>", prefix);

            // configure the instance (server, credentials)
            sb.AppendFormat("{0}    <LobSystemInstances>", prefix);
            sb.AppendFormat("{0}      <LobSystemInstance Name=\"{1}\">", prefix, LobSystemInstanceName);
            sb.AppendFormat("{0}        <Properties>", prefix);
            sb.AppendFormat("{0}          <Property Name=\"SiteURL\" Type=\"System.String\">{1}</Property>", prefix, txtSiteUrl.Text);
            sb.AppendFormat("{0}          <Property Name=\"List\" Type=\"System.String\">{1}</Property>", prefix, ((KeyValuePair<string, Guid>)cmbSiteLists.SelectedItem).Key);
            sb.AppendFormat("{0}          <!-- Credentials is (ANONYMOUS, USER, SYSTEM, or CUSTOM) -->", prefix);
            sb.AppendFormat("{0}          <Property Name=\"Credentials\"         Type=\"System.String\">SYSTEM</Property>", prefix);
            sb.AppendFormat("{0}          <Property Name=\"CredentialsUsername\" Type=\"System.String\"></Property>", prefix);
            sb.AppendFormat("{0}          <Property Name=\"CredentialsPassword\" Type=\"System.String\"></Property>", prefix);
            sb.AppendFormat("{0}          <Property Name=\"CredentialsDomain\"   Type=\"System.String\"></Property>", prefix);
            sb.AppendFormat("{0}        </Properties>", prefix);
            sb.AppendFormat("{0}      </LobSystemInstance>", prefix);
            sb.AppendFormat("{0}    </LobSystemInstances>", prefix);
            sb.Append(GenerateEntities(String.Format("{0}    ", prefix)));
            sb.AppendFormat("{0}  </LobSystem>", prefix);
            sb.AppendFormat("{0}</LobSystems>", prefix);

            return sb.ToString();
        }
        private string GenerateEntities(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            string EntityName = this.txtEntityName.Text;
            string EntityNamespace = this.txtEntityNamespace.Text;
            string EntityVersion = this.txtEntityVersion.Text;

            sb.AppendFormat("{0}<Entities>", prefix);
            sb.AppendFormat("{0}  <Entity Name=\"{1}\" Namespace=\"{2}\" Version=\"{3}\" EstimatedInstanceCount=\"1000\">", prefix, EntityName, EntityNamespace, EntityVersion);

            sb.Append(GenerateIdentities(String.Format("{0}    ", prefix)));
            sb.Append(GenerateMethods(String.Format("{0}    ", prefix)));

            sb.AppendFormat("{0}  </Entity>", prefix);
            sb.AppendFormat("{0}</Entities>", prefix);
            sb.AppendFormat("{0}", prefix);

            return sb.ToString();
        }
        private string GenerateIdentities(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Identifiers>", prefix);

            sb.AppendFormat("{0}  <Identifier Name=\"ID\" TypeName=\"System.Int32\" />", prefix);

            sb.AppendFormat("{0}</Identifiers>", prefix);

            return sb.ToString();
        }
        private string GenerateMethods(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Methods>", prefix);
            sb.Append(GenerateMethod_Finder(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_SpecificFinder(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_IdEnumerator(String.Format("{0}  ", prefix)));
            sb.AppendFormat("{0}</Methods>", prefix);

            return sb.ToString();
        }

        private string GenerateMethod_Finder(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            string MethodName = "ReadAll";
            string MethodDefaultDisplayName = "Read All Items";
            string MethodInstanceName = "AllItems";                 // View FileName => AllItems.aspx
            string MethodInstanceDefaultDisplayName = "All Items";  // View Name

            sb.AppendFormat("{0}<Method Name=\"{1}\" DefaultDisplayName=\"{2}\">", prefix, MethodName, MethodDefaultDisplayName);

            sb.Append(GenerateMethod_Finder_Filters(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_Finder_Parameters(String.Format("{0}  ", prefix), MethodInstanceName));

            sb.AppendFormat("{0}  <MethodInstances>", prefix);
            sb.AppendFormat("{0}    <MethodInstance Type=\"Finder\" Name=\"{1}\" DefaultDisplayName=\"{2}\" ReturnParameterName=\"MultipleRecord\" />", prefix, MethodInstanceName, MethodInstanceDefaultDisplayName);
            sb.AppendFormat("{0}  </MethodInstances>", prefix);

            sb.AppendFormat("{0}</Method>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_Finder_Filters(string prefix)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("{0}<FilterDescriptors>", prefix);

            foreach (var gvr in dgvr_Finder_Filters)
            {
                sb.AppendFormat("{0}  <FilterDescriptor Type=\"Wildcard\" Name=\"{1}\">", prefix, gvr.StaticName);

                // localized displayname
                sb.AppendFormat("{0}    <LocalizedDisplayNames><LocalizedDisplayName LCID=\"1033\">{1}</LocalizedDisplayName></LocalizedDisplayNames>", prefix, gvr.Name);

                sb.AppendFormat("{0}  </FilterDescriptor>", prefix);
            }
            sb.AppendFormat("{0}  <FilterDescriptor Type=\"Limit\" Name=\"RowLimit\" />", prefix);
            sb.AppendFormat("{0}</FilterDescriptors>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_Finder_Parameters(string prefix, string MethodInstanceName)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameters>", prefix);
            sb.Append(GenerateMethod_Finder_Parameters_Input(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_Finder_Parameters_Output(String.Format("{0}  ", prefix), MethodInstanceName));
            sb.AppendFormat("{0}</Parameters>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_Finder_Parameters_Input(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(GenerateInputParameters(dgvr_Finder_Filters, prefix));
            sb.AppendFormat("{0}<Parameter Direction=\"In\" Name=\"RowLimit\">", prefix);
            sb.AppendFormat("{0}  <TypeDescriptor Name=\"RowLimit\" TypeName=\"System.Int32\" AssociatedFilter=\"RowLimit\" IsCollection=\"false\" />", prefix);
            sb.AppendFormat("{0}</Parameter>", prefix);
            //sb.AppendFormat("{0}", prefix);
            //sb.AppendFormat("{0}", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_Finder_Parameters_Output(string prefix, string MethodInstanceName)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameter Direction=\"Return\" Name=\"MultipleRecord\">", prefix);
            sb.AppendFormat("{0}  <TypeDescriptor Name=\"Records\" TypeName=\"Microsoft.BusinessData.Runtime.DynamicType[]\" IsCollection=\"true\">", prefix);
            sb.AppendFormat("{0}    <TypeDescriptors>", prefix);
            sb.Append(GenerateOutputParameters(dgvr_Finder, String.Format("{0}      ", prefix), MethodInstanceName));
            sb.AppendFormat("{0}    </TypeDescriptors>", prefix);
            sb.AppendFormat("{0}  </TypeDescriptor>", prefix);
            sb.AppendFormat("{0}</Parameter>", prefix);

            return sb.ToString();
        }

        private string GenerateMethod_SpecificFinder(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            string MethodName = "ReadOne";
            string MethodDefaultDisplayName = "Read One Item";
            string MethodInstanceName = "DispItem";
            string MethodInstanceDefaultDisplayName = "Item";

            sb.AppendFormat("{0}<Method Name=\"{1}\" DefaultDisplayName=\"{2}\">", prefix, MethodName, MethodDefaultDisplayName);

            sb.Append(GenerateMethod_SpecificFinder_Filters(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_SpecificFinder_Parameters(String.Format("{0}  ", prefix), MethodInstanceName));

            sb.AppendFormat("{0}  <MethodInstances>", prefix);
            sb.AppendFormat("{0}    <MethodInstance Type=\"SpecificFinder\" Name=\"{1}\" DefaultDisplayName=\"{2}\" ReturnParameterName=\"Record\" />", prefix, MethodInstanceName, MethodInstanceDefaultDisplayName);
            sb.AppendFormat("{0}  </MethodInstances>", prefix);

            sb.AppendFormat("{0}</Method>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_SpecificFinder_Filters(string prefix)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("{0}<FilterDescriptors>", prefix);

            foreach (var gvr in dgvr_SpecificFinder_Filters)
            {
                sb.AppendFormat("{0}  <FilterDescriptor Type=\"Comparison\" Name=\"{1}\" />", prefix, gvr.Name);
            }

            sb.AppendFormat("{0}</FilterDescriptors>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_SpecificFinder_Parameters(string prefix, string MethodInstanceName)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameters>", prefix);
            sb.Append(GenerateMethod_SpecificFinder_Parameters_Input(String.Format("{0}  ", prefix)));
            sb.Append(GenerateMethod_SpecificFinder_Parameters_Output(String.Format("{0}  ", prefix), MethodInstanceName));
            sb.AppendFormat("{0}</Parameters>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_SpecificFinder_Parameters_Input(string prefix)
        {
            return GenerateInputParameters(dgvr_SpecificFinder_Filters, prefix);
        }
        private string GenerateMethod_SpecificFinder_Parameters_Output(string prefix, string MethodInstanceName)
        {
            // MethodInstanceName = "DispForm"
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameter Direction=\"Return\" Name=\"Record\">", prefix);
            sb.Append(GenerateOutputParameters(dgvr_SpecificFinder ,String.Format("{0}  ", prefix), MethodInstanceName));
            sb.AppendFormat("{0}</Parameter>", prefix);

            return sb.ToString();
        }

        private string GenerateMethod_IdEnumerator(string prefix)
        {
            StringBuilder sb = new StringBuilder();

            string MethodName = "ReadIds";
            string MethodDefaultDisplayName = "Read IDs";
            string MethodInstanceName = "AllIds";
            string MethodInstanceDefaultDisplayName = "Multiple Id Finder";
            string ReturnParameterName = "MultipleRecordIds";

            sb.AppendFormat("{0}<Method Name=\"{1}\" DefaultDisplayName=\"{2}\">", prefix, MethodName, MethodDefaultDisplayName);

            sb.Append(GenerateMethod_IdEnumerator_Parameters(String.Format("{0}  ", prefix), MethodInstanceName, ReturnParameterName));

            sb.AppendFormat("{0}  <MethodInstances>", prefix);
            sb.AppendFormat("{0}    <MethodInstance Type=\"IdEnumerator\" Name=\"{1}\" DefaultDisplayName=\"{2}\" ReturnParameterName=\"{3}\" />", prefix, MethodInstanceName, MethodInstanceDefaultDisplayName, ReturnParameterName);
            sb.AppendFormat("{0}  </MethodInstances>", prefix);

            sb.AppendFormat("{0}</Method>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_IdEnumerator_Parameters(string prefix, string MethodInstanceName, string ReturnParameterName)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameters>", prefix);
            sb.Append(GenerateMethod_IdEnumerator_Parameters_Output(String.Format("{0}  ", prefix), MethodInstanceName, ReturnParameterName));
            sb.AppendFormat("{0}</Parameters>", prefix);

            return sb.ToString();
        }
        private string GenerateMethod_IdEnumerator_Parameters_Output(string prefix, string MethodInstanceName, string ReturnParameterName)
        {
            // MethodInstanceName = "DispForm"
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<Parameter Direction=\"Return\" Name=\"{1}\">", prefix, ReturnParameterName);
            sb.AppendFormat("{0}  <TypeDescriptor Name=\"Records\" TypeName=\"Microsoft.BusinessData.Runtime.DynamicType[]\" IsCollection=\"true\">", prefix);
            sb.AppendFormat("{0}    <TypeDescriptors>", prefix);
            sb.Append(GenerateOutputParameters(dgvr_Enumerator, String.Format("{0}      ", prefix), MethodInstanceName));
            sb.AppendFormat("{0}    </TypeDescriptors>", prefix);
            sb.AppendFormat("{0}  </TypeDescriptor>", prefix);
            sb.AppendFormat("{0}</Parameter>", prefix);

            return sb.ToString();
        }

        private string GenerateInputParameters(IEnumerable<dgvRecord> records,string prefix)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var gvr in records)
            {
                sb.AppendFormat("{0}<Parameter Direction=\"In\" Name=\"{1}\">",
                                prefix,
                                gvr.StaticName);
                sb.AppendFormat("{0}  <TypeDescriptor Name=\"{1}\" TypeName=\"{2}\" AssociatedFilter=\"{1}\" IsCollection=\"false\">",
                                prefix,
                                gvr.StaticName,
                                gvr.DataType,
                                gvr.Name);
                sb.AppendFormat("{0}    <LocalizedDisplayNames><LocalizedDisplayName LCID=\"1033\">{1}</LocalizedDisplayName></LocalizedDisplayNames>",
                                prefix,
                                gvr.Name);
                sb.AppendFormat("{0}  </TypeDescriptor>", prefix);
                sb.AppendFormat("{0}</Parameter>", prefix);

            }

            return sb.ToString();
        }
        private string GenerateOutputParameters(IEnumerable<dgvRecord> records, string prefix, string MethodInstanceName)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}<TypeDescriptor Name=\"Record\" TypeName=\"Microsoft.BusinessData.Runtime.DynamicType\">", prefix);
            sb.AppendFormat("{0}  <TypeDescriptors>", prefix);

            foreach (var gvr in records)
            {
                string TypeDescriptorDataType = gvr.DataTypeString == "System.DateTime" ? "System.String" : gvr.DataTypeString;

                if (gvr.IsIdentifier.HasValue && gvr.IsIdentifier.Value)
                    sb.AppendFormat("{0}    <TypeDescriptor Name=\"{1}\" IdentifierName=\"{1}\" ReadOnly=\"true\" TypeName=\"{2}\">", prefix, gvr.StaticName, TypeDescriptorDataType);
                else
                    sb.AppendFormat("{0}    <TypeDescriptor Name=\"{1}\"                        ReadOnly=\"true\" TypeName=\"{2}\">", prefix, gvr.StaticName, TypeDescriptorDataType);

                // localized displayname
                sb.AppendFormat("{0}      <LocalizedDisplayNames><LocalizedDisplayName LCID=\"1033\">{1}</LocalizedDisplayName></LocalizedDisplayNames>", prefix, gvr.Name);

                // Properties
                List<string> Properties = new List<string>();

                // Format String
                if (!String.IsNullOrWhiteSpace(gvr.FormatString))
                    Properties.Add(String.Format("<Property Name=\"FormatString\" Type=\"System.String\">{0}</Property>", gvr.FormatString));

                // ShowInPicker and DisplayByDefault
                if (!(gvr.ShowInFinder.HasValue && gvr.ShowInFinder.Value) && 
                    !(gvr.ShowInSpecificFinder.HasValue && gvr.ShowInSpecificFinder.Value))
                {
                    Properties.Add(String.Format("<Property Name=\"DisplayByDefault\" Type=\"System.Boolean\">false</Property>"));
                    Properties.Add(String.Format("<Property Name=\"ShowInPicker\" Type=\"System.Boolean\">false</Property>"));
                }

                // add properties using best formatting
                if (Properties.Count == 1)
                {
                    sb.AppendFormat("{0}      <Properties>{1}</Properties>", prefix, Properties[0]);
                }
                else if (Properties.Count > 1)
                {
                    sb.AppendFormat("{0}      <Properties>", prefix);
                    Properties.ForEach(Property => sb.AppendFormat("{0}          {1}", prefix, Property));
                    sb.AppendFormat("{0}      </Properties>", prefix);
                }

                // Format String
                //if (!String.IsNullOrWhiteSpace(gvr.FormatString))
                //    sb.AppendFormat("{0}      <Properties><Property Name=\"FormatString\" Type=\"{1}\">{2}</Property></Properties>", prefix, gvr.DataTypeString, gvr.FormatString);

                // default value
                //sb.AppendFormat("{0}      <DefaultValues><DefaultValue MethodInstanceName=\"{1}\" Type=\"{2}\">{3}</DefaultValue></DefaultValues>", prefix, MethodInstanceName, gvr.DataTypeString, Default(gvr.DataType));
                
                sb.AppendFormat("{0}    </TypeDescriptor>", prefix);

            }

            sb.AppendFormat("{0}  </TypeDescriptors>", prefix);
            sb.AppendFormat("{0}</TypeDescriptor>", prefix);

            return sb.ToString();
        }

        #endregion
    }
}
